/**
 * segment - A JavaScript library to draw and animate SVG path strokes
 * @version v1.1.3
 * @link https://github.com/lmgonzalves/segment
 * @license MIT
 */

(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = factory();
    } else {
        root.Segment = factory();
    }
}(this, function () {

    function Segment(path, begin, end, circular){
        this.path = path;
        this.reset();
        this.begin = typeof begin !== 'undefined' ? this.valueOf(begin) : 0;
        this.end = typeof end !== 'undefined' ? this.valueOf(end) : this.length;
        this.circular = typeof circular !== 'undefined' ? circular : false;
        this.timer = null;
        this.animationTimer = null;
        this.draw(this.begin, this.end, 0, {circular: this.circular});
    }

    Segment.prototype = {
        reset: function(){
            this.length = this.path.getTotalLength();
            this.path.style.strokeDashoffset = this.length * 2;
        },

        draw: function(begin, end, duration, options){
            this.circular = options && options.hasOwnProperty('circular') ? options.circular : false;
            if(duration){
                this.stop();

                var that = this;
                var delay = options && options.hasOwnProperty('delay') ? parseFloat(options.delay) * 1000 : 0;
                if(delay){
                    delete options.delay;
                    this.timer = setTimeout(function(){
                        that.draw(begin, end, duration, options);
                    }, delay);
                    return this.timer;
                }

                this.startTime = new Date();
                this.initBegin = this.begin;
                this.initEnd = this.end;
                this.finalBegin = this.valueOf(begin);
                this.finalEnd = this.valueOf(end);
                this.pausedTime = 0;
                this.duration = duration;
                this.easing = options && options.hasOwnProperty('easing') ? options.easing : null;
                this.update = options && options.hasOwnProperty('update') ? options.update : null;
                this.callback = options && options.hasOwnProperty('callback') ? options.callback : null;

                this.animationTimer = requestAnimationFrame(this.play.bind(this));
            }else{
                this.path.style.strokeDasharray = this.strokeDasharray(begin, end);
            }
        },

        play: function() {
            var now = new Date();
            if (this.pausedTime) {
                this.startTime.setMilliseconds(this.startTime.getMilliseconds() + (now - this.pausedTime));
                this.pausedTime = 0;
            }
            var elapsed = (now - this.startTime) / 1000;
            var time = (elapsed / parseFloat(this.duration));
            var t = time;

            if(typeof this.easing === 'function'){
                t = this.easing(t);
            }

            if(time > 1){
                t = 1;
                this.stop();
            }else{
                this.animationTimer = requestAnimationFrame(this.play.bind(this));
            }

            this.drawStep(t);

            if(time > 1 && typeof this.callback === 'function'){
                return this.callback.call(this);
            }
        },

        pause: function() {
            if (this.animationTimer) {
                this.stop();
                this.pausedTime = new Date();
            }
        },

        resume: function() {
            if (!this.animationTimer) {
                this.animationTimer = requestAnimationFrame(this.play.bind(this));
            }
        },

        stop: function(){
            cancelAnimationFrame(this.animationTimer);
            this.animationTimer = null;
            clearTimeout(this.timer);
            this.timer = null;
        },

        drawStep: function(t) {
            this.begin = this.initBegin + (this.finalBegin - this.initBegin) * t;
            this.end = this.initEnd + (this.finalEnd - this.initEnd) * t;

            this.begin = this.begin < 0 && !this.circular ? 0 : this.begin;
            this.begin = this.begin > this.length && !this.circular ? this.length : this.begin;
            this.end = this.end < 0 && !this.circular ? 0 : this.end;
            this.end = this.end > this.length && !this.circular ? this.length : this.end;

            if (this.end - this.begin <= this.length && this.end - this.begin > 0) {
                this.draw(this.begin, this.end, 0, {circular: this.circular});
            } else {
                if (this.circular && this.end - this.begin > this.length) {
                    this.draw(0, this.length, 0, {circular: this.circular});
                } else {
                    this.draw(this.begin + (this.end - this.begin), this.end - (this.end - this.begin), 0, {circular: this.circular});
                }
            }

            if(typeof this.update === 'function'){
                this.update(this);
            }
        },

        strokeDasharray: function(begin, end){
            this.begin = this.valueOf(begin);
            this.end = this.valueOf(end);
            if(this.circular){
                var division = this.begin > this.end || (this.begin < 0 && this.begin < this.length * -1)
                    ? parseInt(this.begin / parseInt(this.length)) : parseInt(this.end / parseInt(this.length));
                if(division !== 0){
                    this.begin = this.begin - this.length * division;
                    this.end = this.end - this.length * division;
                }
            }
            if(this.end > this.length){
                var plus = this.end - this.length;
                return [this.length, this.length, plus, this.begin - plus, this.end - this.begin].join(' ');
            }
            if(this.begin < 0){
                var minus = this.length + this.begin;
                if(this.end < 0){
                    return [this.length, this.length + this.begin, this.end - this.begin, minus - this.end, this.end - this.begin, this.length].join(' ');
                }else{
                    return [this.length, this.length + this.begin, this.end - this.begin, minus - this.end, this.length].join(' ');
                }
            }
            return [this.length, this.length + this.begin, this.end - this.begin].join(' ');
        },

        valueOf: function(input){
            var val = parseFloat(input);
            if(typeof input === 'string' || input instanceof String){
                if(~input.indexOf('%')){
                    var arr;
                    if(~input.indexOf('+')){
                        arr = input.split('+');
                        val = this.percent(arr[0]) + parseFloat(arr[1]);
                    }else if(~input.indexOf('-')){
                        arr = input.split('-');
                        if(arr.length === 3){
                            val = -this.percent(arr[1]) - parseFloat(arr[2]);
                        }else{
                            val = arr[0] ? this.percent(arr[0]) - parseFloat(arr[1]) : -this.percent(arr[1]);
                        }
                    }else{
                        val = this.percent(input);
                    }
                }
            }
            return val;
        },

        percent: function(value){
            return parseFloat(value) / 100 * this.length;
        }
    };

    return Segment;

}));
